// fitsconverter.cpp
#include "fitsconverter.h"
#include "fitsio.h"
#include <fstream>
#include <stdexcept>
#include <vector>
#include <cstring>
#include <algorithm>
#include <memory>
#include <sys/stat.h>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QRegularExpression>
#include <QDateTime>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonObject>
#include <QValueAxis>
#include <csignal>  // 添加这个头文件
#include <QNetworkInterface>  // 网络接口相关
#include <QHostAddress>       // IP地址处理
#include <QDebug>
#include <string>
#include <map>
#include <variant>

using HeaderValue = std::variant<std::string, int, double>;
using HeaderKeyword = std::tuple<std::string, HeaderValue, std::string>;
// 格式: (关键字名, 值, 注释)
using HeaderKeywords = std::vector<HeaderKeyword>;


// 生成主HDU的标准头信息（预留所有关键字位置）
HeaderKeywords generatePrimaryHeader(const QString& filename,
                                     const QString& pathname,
                                     const QString& imagetype,
                                     const QString& date_,
                                     const QString& readmode,
                                     const QString& channel,
                                     const double& TEMP_1,
                                     const double& TEMP_2,
                                     const double& TEMP_3,
                                     const double& TEMP_4,
                                     const double& TEMP,const double& EXPTIME, const double& EXPREQ, const QString& EXPSTA, const QString& EXPEND) {

    HeaderKeywords keywords;

    // 基础信息（必须关键字）
    //keywords.emplace_back("SIMPLE", "T", "File contains extensions.");
    //keywords.emplace_back("BITPIX", 8, "number of bits per data pixel,indicates array format."); // NAXIS=0时必须为8
    // keywords.emplace_back("NAXIS", 0, "number of data axes.");
    //keywords.emplace_back("EXTEND", "T", "File contains extensions.");
    //keywords.emplace_back("NEXTEND", 4, "Number of extensions");

    //QString mergedFitsPath = fitsSavePath + "/" + currentBaseFilename +".fits";
    // 图像信息
    keywords.emplace_back("DETSIZE", "[1:20000,1:18800]", "Total data pixels in full mosaic.");
    keywords.emplace_back("FILENAME", filename.toStdString(), "The file name");
    keywords.emplace_back("PATHNAME", pathname.toStdString(), "Original directory name at acquisiti");

    keywords.emplace_back("IMAGETYP", imagetype.toStdString(), "image type");
    keywords.emplace_back("HBIN", 1, "Binning factor along first axis，Horizontal binning.");
    keywords.emplace_back("VBIN", 1, "Binning factor along second axis，Vertical binning.");
    keywords.emplace_back("DUNIT", "ADU", "The unit of this image.");
    keywords.emplace_back("DATE", EXPSTA.toStdString(), "Time at start of exposure/dark (YYYY-MM-DDThh:mm:ss.sss)");
    keywords.emplace_back("FG_DATE", date_.toStdString(), "UTC Date of file creation.");
    keywords.emplace_back("NCCDS", 4, "Number of devices.");
    keywords.emplace_back("NAMPS", 64, "Number of amplifiers.");
    keywords.emplace_back("READMODE", readmode.toStdString(), "Readout mode.");
    keywords.emplace_back("CHANNEL", channel.toStdString(), "Channel used.");
    keywords.emplace_back("FILTER", "", "Name of filter in position.");

    // 观测信息
    keywords.emplace_back("OBSERVER", "MephistoTeam", "Observer(s).");
    keywords.emplace_back("OBJECT", "", "Observation object.");
    keywords.emplace_back("TARGET", "", "Target coordinates");
    keywords.emplace_back("FIELDID", "", "Field ID.");
    keywords.emplace_back("OBJ_RA", "", "Right ascension of object.");
    keywords.emplace_back("OBJ_DEC", "", "Declination of object.");
    keywords.emplace_back("RAD", "", "Right ascension in degress");
    keywords.emplace_back("DECD", "", "Declination in degrees.");

    // 探测器信息
    keywords.emplace_back("DLLVER", "MephCam v0.1", "Image creation software version");//ics V0???
    keywords.emplace_back("DETECTOR", "MephCam", "Detector type.");
    keywords.emplace_back("CCD", "E2V CCD290", "CCD manufacturer and model");
    keywords.emplace_back("MAXLIN", 65535, "Maximum linearity value(ADU)");
    keywords.emplace_back("QEPOINTS", "", "QE%@wavelength (nm).");//???
    keywords.emplace_back("TIMESYS", "UTC", "Time System for DATExxxx and TIMExxxx.");
    // 曝光信息
    keywords.emplace_back("EXPTIME", EXPTIME, "Measured integration time(seconds).");
    keywords.emplace_back("EXPREQ", EXPREQ, "Requested integration time (seconds).");
    keywords.emplace_back("EXPSTA", EXPSTA.toStdString(), "Time at start of exposure/dark (YYYY-MM-DDThh:mm:ss.sss)");
    keywords.emplace_back("EXPEND", EXPEND.toStdString(), "Time at end of exposure/dark (YYYY-MM-DDThh:mm:ss.sss");
    keywords.emplace_back("OBSID", 20, "Mephisto observation ID number.");

    // 温度信息
    keywords.emplace_back("TEMP_1", TEMP_1, "Temperature of CCD1");
    keywords.emplace_back("TEMP_2", TEMP_2, "Temperature of CCD2");
    keywords.emplace_back("TEMP_3", TEMP_3, "Temperature of CCD3");
    keywords.emplace_back("TEMP_4", TEMP_4, "Temperature of CCD4");
    keywords.emplace_back("TEMP", TEMP, "Average temperature of 4 CCDs");

    // 望远镜信息
    keywords.emplace_back("TELESCOP", "MEPHISTO", "Telescope of the data.");
    keywords.emplace_back("OBSERVAT", "Lijiang Observatory(GMG)", "Observation site");
    keywords.emplace_back("OBSLON", 100.0313, "Observatory longitude(deg)");
    keywords.emplace_back("OBSLAT", 26.6974, "Observatory latitude(deg)");
    keywords.emplace_back("OBSALT", 3227, "Observatory altitude(meter)");
    keywords.emplace_back("TEL_RA", "", "Right ascension of telescope.");
    keywords.emplace_back("TEL_DEC", "", "Declination of telescope.");
    keywords.emplace_back("ROT", "", "Rotation angle of telescope.");
    keywords.emplace_back("TRACKRA", "", "Right ascension tracking state");
    keywords.emplace_back("TRACKDEC", "", "Declination tracking state.");
    keywords.emplace_back("RAPRECI", "", "Precision of right ascension (degrees).");
    keywords.emplace_back("DECPRECI", "", "Precision of declination (degrees)");
    keywords.emplace_back("AIRMASS", "", "Airmass at observation time");
    keywords.emplace_back("OBS_JD", "", "Julian Date of observation.");
    keywords.emplace_back("M2POS1", "", "M2 parameter 1");
    keywords.emplace_back("M2POS2", "", "M2 parameter 2");
    keywords.emplace_back("M2POS3", "", "M2 parameter 3");
    keywords.emplace_back("M2POS4", "", "M2 parameter 4");
    keywords.emplace_back("M2POS5", "", "M2 parameter 5");
    keywords.emplace_back("AZ", "", "azimuth");
    keywords.emplace_back("ALT", "", "altitude");
    keywords.emplace_back("DOMESTAT", "", "Dome status at beginning of exposure");
    keywords.emplace_back("DOME_AZ", "", "Dome azimuth (deg)");

    // 天气信息
    keywords.emplace_back("TEMPTURE", "", "Outside air temperature(C)");
    keywords.emplace_back("WINDSPD", "", "Outside wind speed");
    keywords.emplace_back("WINDDIR", "", "Outside wind direction(deg)");
    keywords.emplace_back("DEWPOINT", "", "Dewpoint (C)");
    keywords.emplace_back("HUMIDITY", "", "Humidity");
    keywords.emplace_back("PRESSURE", "", "Atmospheric pressure,millibars");
    keywords.emplace_back("CLOUDS", "", "Cloud sensor reading");
    keywords.emplace_back("SEEING", "", "seeing");
    keywords.emplace_back("MOONRA", "", "Moon J2000.0 R.A. (deg)");
    keywords.emplace_back("MOONDEC", "", "Moon J2000.0 Dec. (deg)");
    keywords.emplace_back("MOONALT", "", "Moon altitude (deg)");
    keywords.emplace_back("MOONILLF", "", "Moon illuminated fraction(frac)");
    keywords.emplace_back("MOONPHAS", "", "Moon phase angle (deg)");

    return keywords;
}

// 生成扩展HDU的标准头信息（预留所有关键字位置）
HeaderKeywords generateExtensionHeader(int ccdIndex,
                                       const QString& CCD_SN,
                                       const QString& imagetype,
                                       const QString& imgrect,
                                       const QString& readmode,
                                       const QString& channel,
                                       const double& EXPTIME, const double& EXPREQ, const QString& EXPSTA, const QString& EXPEND) {
    HeaderKeywords keywords;

    // 基础信息
    keywords.emplace_back("EXTNAME", "CCD-" + std::to_string(ccdIndex+1), "Extension name");
    keywords.emplace_back("EXTVER", ccdIndex+1, "Extension index");
    keywords.emplace_back("DUNIT", "ADU", "The unit of this image.");
    keywords.emplace_back("BZERO", 32768, "");

    // CCD特定信息
    keywords.emplace_back("CCD_SN", CCD_SN.toStdString(), "Serial Number of CCD");//?????
    keywords.emplace_back("CCD_ID", std::to_string(ccdIndex+1), "ID value of CCD amplifier.");
    keywords.emplace_back("TEMP_" + std::to_string(ccdIndex+1), -100.0019, "Temperature of CCD.");

    // 曝光信息
    keywords.emplace_back("EXPTIME", EXPTIME, "Measured integration time(seconds).");
    keywords.emplace_back("EXPREQ", EXPREQ, "Requested integration time (seconds).");
    keywords.emplace_back("EXPSTA", EXPSTA.toStdString(), "Time at start of exposure/dark (YYYY-MM-DDThh:mm:ss.sss");
    keywords.emplace_back("EXPEND", EXPEND.toStdString(), "Time at end of exposure/dark (YYYY-MM-DDThh:mm:ss.sss");

    // 图像信息
    keywords.emplace_back("IMAGETYP", imagetype.toStdString(), "Image Type.");
    keywords.emplace_back("IMGRECT", imgrect.toStdString(), "Subimage format.");
    keywords.emplace_back("RDMODE", readmode.toStdString(), "Readout mode");
    keywords.emplace_back("SATURATE", 65535, "Saturate value (ADU).");
    keywords.emplace_back("PORTMODE", 16, "Port Readout Mode.");
    keywords.emplace_back("OBJECT", "", "Object name.");
    keywords.emplace_back("OBSID", "", "Observation ID.");
    keywords.emplace_back("CHANNEL", channel.toStdString(), "Channel used.");
    keywords.emplace_back("FILTER", "", "Name of filter in position");
    keywords.emplace_back("OBJ_RA", "", "Right ascension of object");
    keywords.emplace_back("OBJ_DEC", "", "Declination of object.");
    keywords.emplace_back("RAD", "", "Right ascension in degress");
    keywords.emplace_back("DECD", "", "Declination in degrees.");
    keywords.emplace_back("TEL_RA", "", "Right ascension of telescope.");
    keywords.emplace_back("TEL_DEC", "", "Declination ascension of telescope.");
    keywords.emplace_back("HBIN", 1, "Horizontal binning.");
    keywords.emplace_back("VBIN", 1, "Vertical binning.");
    keywords.emplace_back("PIXSCAL1", 0.286, "Pixel size for axis 1(arcsec/pixel).");
    keywords.emplace_back("PIXSCAL2", 0.286, "Pixel size for axis 2(arcsec/pixel).");
    keywords.emplace_back("PIXSIZE1", 10, "Pixel size for axis 1(microns).");
    keywords.emplace_back("PIXSIZE2", 10, "Pixel size for axis 2(microns).");
    keywords.emplace_back("EPOCH", 2000.0, "EPOCH.");
    keywords.emplace_back("EQUINOX", "J2000", "Equinox of WCS");
    keywords.emplace_back("WCSDIM", 2, "number of bits per data pixel");

    // WCS信息
    keywords.emplace_back("CTYPE1", "RA---TAN", "Coordinate type");
    keywords.emplace_back("CTYPE2", "DEC--TAN", "Coordinate type");
    keywords.emplace_back("CRVAL1", "", "Coordinate reference value");
    keywords.emplace_back("CRVAL2", "", "Coordinate reference value");
    keywords.emplace_back("CRPIX1", "", "Coordinate reference pixel");
    keywords.emplace_back("CRPIX2", "", "Coordinate reference pixel");
    keywords.emplace_back("CD1_1", "", "Coordinate matrix");
    keywords.emplace_back("CD1_2", "",  "Coordinate matrix");
    keywords.emplace_back("CD2_1", "",  "Coordinate matrix");
    keywords.emplace_back("CD2_2", "",  "Coordinate matrix");

    return keywords;
}

// 生成主HDU的标准头信息（预留所有关键字位置）
HeaderKeywords generatePrimaryHeader4720(const QString& filename,
                                         const QString& pathname,
                                         const QString& imagetype,
                                         const QString& date_,
                                         const QString& channel,
                                         const double& EXPREQ) {

    HeaderKeywords keywords;

    // 基础信息（必须关键字）
    //keywords.emplace_back("SIMPLE", "T", "File contains extensions.");
    //keywords.emplace_back("BITPIX", 8, "number of bits per data pixel,indicates array format."); // NAXIS=0时必须为8
    // keywords.emplace_back("NAXIS", 0, "number of data axes.");
    //keywords.emplace_back("EXTEND", "T", "File contains extensions.");
    //keywords.emplace_back("NEXTEND", 4, "Number of extensions");

    //QString mergedFitsPath = fitsSavePath + "/" + currentBaseFilename +".fits";
    // 图像信息
    keywords.emplace_back("DETSIZE", "[1:20000,1:18800]", "Total data pixels in full mosaic.");
    keywords.emplace_back("FILENAME", filename.toStdString(), "The file name");
    keywords.emplace_back("PATHNAME", pathname.toStdString(), "Original directory name at acquisiti");

    keywords.emplace_back("IMAGETYP", imagetype.toStdString(), "image type");
    keywords.emplace_back("HBIN", 1, "Binning factor along first axis，Horizontal binning.");
    keywords.emplace_back("VBIN", 1, "Binning factor along second axis，Vertical binning.");
    keywords.emplace_back("DUNIT", "ADU", "The unit of this image.");

    keywords.emplace_back("FG_DATE", date_.toStdString(), "UTC Date of file creation.");
    keywords.emplace_back("NCCDS", 4, "Number of devices.");
    keywords.emplace_back("NAMPS", 64, "Number of amplifiers.");

    keywords.emplace_back("CHANNEL", channel.toStdString(), "Channel used.");
    keywords.emplace_back("FILTER", "", "Name of filter in position.");

    // 观测信息
    keywords.emplace_back("OBSERVER", "MephistoTeam", "Observer(s).");
    keywords.emplace_back("OBJECT", "", "Observation object.");
    keywords.emplace_back("TARGET", "", "Target coordinates");
    keywords.emplace_back("FIELDID", "", "Field ID.");
    keywords.emplace_back("OBJ_RA", "", "Right ascension of object.");
    keywords.emplace_back("OBJ_DEC", "", "Declination of object.");
    keywords.emplace_back("RAD", "", "Right ascension in degress");
    keywords.emplace_back("DECD", "", "Declination in degrees.");

    // 探测器信息
    keywords.emplace_back("DLLVER", "MephCam v0.1", "Image creation software version");//ics V0???
    keywords.emplace_back("DETECTOR", "MephCam", "Detector type.");
    keywords.emplace_back("CCD", "E2V CCD290", "CCD manufacturer and model");
    keywords.emplace_back("MAXLIN", 65535, "Maximum linearity value(ADU)");
    keywords.emplace_back("QEPOINTS", "", "QE%@wavelength (nm).");//???
    keywords.emplace_back("TIMESYS", "UTC", "Time System for DATExxxx and TIMExxxx.");
    // 曝光信息

    keywords.emplace_back("EXPREQ", EXPREQ, "Requested integration time (seconds).");

    keywords.emplace_back("OBSID", 20, "Mephisto observation ID number.");


    // 望远镜信息
    keywords.emplace_back("TELESCOP", "MEPHISTO", "Telescope of the data.");
    keywords.emplace_back("OBSERVAT", "Lijiang Observatory(GMG)", "Observation site");
    keywords.emplace_back("OBSLON", 100.0313, "Observatory longitude(deg)");
    keywords.emplace_back("OBSLAT", 26.6974, "Observatory latitude(deg)");
    keywords.emplace_back("OBSALT", 3227, "Observatory altitude(meter)");
    keywords.emplace_back("TEL_RA", "", "Right ascension of telescope.");
    keywords.emplace_back("TEL_DEC", "", "Declination of telescope.");
    keywords.emplace_back("ROT", "", "Rotation angle of telescope.");
    keywords.emplace_back("TRACKRA", "", "Right ascension tracking state");
    keywords.emplace_back("TRACKDEC", "", "Declination tracking state.");
    keywords.emplace_back("RAPRECI", "", "Precision of right ascension (degrees).");
    keywords.emplace_back("DECPRECI", "", "Precision of declination (degrees)");
    keywords.emplace_back("AIRMASS", "", "Airmass at observation time");
    keywords.emplace_back("OBS_JD", "", "Julian Date of observation.");
    keywords.emplace_back("M2POS1", "", "M2 parameter 1");
    keywords.emplace_back("M2POS2", "", "M2 parameter 2");
    keywords.emplace_back("M2POS3", "", "M2 parameter 3");
    keywords.emplace_back("M2POS4", "", "M2 parameter 4");
    keywords.emplace_back("M2POS5", "", "M2 parameter 5");
    keywords.emplace_back("AZ", "", "azimuth");
    keywords.emplace_back("ALT", "", "altitude");
    keywords.emplace_back("DOMESTAT", "", "Dome status at beginning of exposure");
    keywords.emplace_back("DOME_AZ", "", "Dome azimuth (deg)");

    // 天气信息
    keywords.emplace_back("TEMPTURE", "", "Outside air temperature(C)");
    keywords.emplace_back("WINDSPD", "", "Outside wind speed");
    keywords.emplace_back("WINDDIR", "", "Outside wind direction(deg)");
    keywords.emplace_back("DEWPOINT", "", "Dewpoint (C)");
    keywords.emplace_back("HUMIDITY", "", "Humidity");
    keywords.emplace_back("PRESSURE", "", "Atmospheric pressure,millibars");
    keywords.emplace_back("CLOUDS", "", "Cloud sensor reading");
    keywords.emplace_back("SEEING", "", "seeing");
    keywords.emplace_back("MOONRA", "", "Moon J2000.0 R.A. (deg)");
    keywords.emplace_back("MOONDEC", "", "Moon J2000.0 Dec. (deg)");
    keywords.emplace_back("MOONALT", "", "Moon altitude (deg)");
    keywords.emplace_back("MOONILLF", "", "Moon illuminated fraction(frac)");
    keywords.emplace_back("MOONPHAS", "", "Moon phase angle (deg)");

    return keywords;
}

// 生成扩展HDU的标准头信息（预留所有关键字位置）
HeaderKeywords generateExtensionHeader4720(int ccdIndex,
                                           const QString& CCD_SN,
                                           const QString& imagetype,
                                           const QString& imgrect,

                                           const QString& channel,
                                           const double& EXPREQ) {
    HeaderKeywords keywords;

    // 基础信息
    keywords.emplace_back("EXTNAME", "CCD-47-20" , "Extension name");
    keywords.emplace_back("EXTVER", ccdIndex+1, "Extension index");
    keywords.emplace_back("DUNIT", "ADU", "The unit of this image.");
    keywords.emplace_back("BZERO", 32768, "");

    // CCD特定信息
    keywords.emplace_back("CCD_SN", CCD_SN.toStdString(), "Serial Number of CCD");//?????
    keywords.emplace_back("CCD_ID", std::to_string(ccdIndex+1), "ID value of CCD amplifier.");

    // 曝光信息

    keywords.emplace_back("EXPREQ", EXPREQ, "Requested integration time (seconds).");


    // 图像信息
    keywords.emplace_back("IMAGETYP", imagetype.toStdString(), "Image Type.");
    keywords.emplace_back("IMGRECT", imgrect.toStdString(), "Subimage format.");

    keywords.emplace_back("SATURATE", 65535, "Saturate value (ADU).");
    keywords.emplace_back("PORTMODE", 16, "Port Readout Mode.");
    keywords.emplace_back("OBJECT", "", "Object name.");
    keywords.emplace_back("OBSID", "", "Observation ID.");
    keywords.emplace_back("CHANNEL", channel.toStdString(), "Channel used.");
    keywords.emplace_back("FILTER", "", "Name of filter in position");
    keywords.emplace_back("OBJ_RA", "", "Right ascension of object");
    keywords.emplace_back("OBJ_DEC", "", "Declination of object.");
    keywords.emplace_back("RAD", "", "Right ascension in degress");
    keywords.emplace_back("DECD", "", "Declination in degrees.");
    keywords.emplace_back("TEL_RA", "", "Right ascension of telescope.");
    keywords.emplace_back("TEL_DEC", "", "Declination ascension of telescope.");
    keywords.emplace_back("HBIN", 1, "Horizontal binning.");
    keywords.emplace_back("VBIN", 1, "Vertical binning.");
    keywords.emplace_back("PIXSCAL1", 0.286, "Pixel size for axis 1(arcsec/pixel).");
    keywords.emplace_back("PIXSCAL2", 0.286, "Pixel size for axis 2(arcsec/pixel).");
    keywords.emplace_back("PIXSIZE1", 10, "Pixel size for axis 1(microns).");
    keywords.emplace_back("PIXSIZE2", 10, "Pixel size for axis 2(microns).");
    keywords.emplace_back("EPOCH", 2000.0, "EPOCH.");
    keywords.emplace_back("EQUINOX", "J2000", "Equinox of WCS");
    keywords.emplace_back("WCSDIM", 2, "number of bits per data pixel");

    // WCS信息
    keywords.emplace_back("CTYPE1", "RA---TAN", "Coordinate type");
    keywords.emplace_back("CTYPE2", "DEC--TAN", "Coordinate type");
    keywords.emplace_back("CRVAL1", "", "Coordinate reference value");
    keywords.emplace_back("CRVAL2", "", "Coordinate reference value");
    keywords.emplace_back("CRPIX1", "", "Coordinate reference pixel");
    keywords.emplace_back("CRPIX2", "", "Coordinate reference pixel");
    keywords.emplace_back("CD1_1", "", "Coordinate matrix");
    keywords.emplace_back("CD1_2", "",  "Coordinate matrix");
    keywords.emplace_back("CD2_1", "",  "Coordinate matrix");
    keywords.emplace_back("CD2_2", "",  "Coordinate matrix");

    return keywords;
}

bool MainWindow::mergeFitsFiles4720(const QStringList& inputFiles, const QString& outputFile, const QString& filename, const QString& pathname,
                                    const QString& date_,
                                    const QString& CCD_SN,
                                    const QString& imagetype,
                                    const QString& imgrect,
                                    const QString& channel, const double& EXPREQ) {
    // 转换QStringList到std::vector<std::string>
    std::vector<std::string> stdInputFiles;
    for (const auto& file : inputFiles) {
        stdInputFiles.push_back(file.toStdString());
    }

    // 调用合并函数（仅预留头信息位置）
    return mergeMephistoFitsFiles4720(stdInputFiles, outputFile.toStdString(), filename, pathname, date_,

                                      CCD_SN,
                                      imagetype,
                                      imgrect,

                                      channel,
                                      EXPREQ);
}
bool MainWindow::mergeMephistoFitsFiles4720(const std::vector<std::string>& inputFiles,
                                            const std::string& outputFile, const QString& filename, const QString& pathname,
                                            const QString& date_,

                                            const QString& CCD_SN,
                                            const QString& imagetype,
                                            const QString& imgrect,

                                            const QString& channel,
                                            const double& EXPREQ) {
    fitsfile *fptr = nullptr;
    int status = 0;

    // 1. 创建文件（强制覆盖）
    fits_create_file(&fptr, ("!" + outputFile).c_str(), &status);
    //if (status) goto error;

    // 2. 创建标准主HDU
    long naxes[0] = {};
    fits_create_img(fptr, 8, 0, naxes, &status);  // BITPIX=8, NAXIS=0
    //if (status) goto error;

    // 3. 写入必须的头信息（顺序敏感！）
    //fits_write_imghdr(fptr);  // 写入标准头结构
    fits_update_key_log(fptr, "SIMPLE", TRUE, "conforms to FITS standard", &status);
    fits_update_key_lng(fptr, "BITPIX", 8, nullptr, &status);
    fits_update_key_lng(fptr, "NAXIS", 0, nullptr, &status);
    fits_update_key_log(fptr, "EXTEND", TRUE, nullptr, &status);
    fits_update_key_lng(fptr, "NEXTEND", 4, nullptr, &status);

    // 3. 写入主HDU头信息（预留所有关键字位置）
    HeaderKeywords primaryKeywords = generatePrimaryHeader4720(filename, pathname, imagetype, date_,channel,
                                                               EXPREQ);
    for (const auto& keyword : primaryKeywords) {
        const std::string& key = std::get<0>(keyword);
        const HeaderValue& value = std::get<1>(keyword);
        const std::string& comment = std::get<2>(keyword);

        std::visit([&](auto&& arg) {
            using T = std::decay_t<decltype(arg)>;

            if constexpr (std::is_same_v<T, std::string>) {
                // 字符串处理
                std::string sanitized;
                for (char c : arg) {
                    sanitized += (isprint(c) && c < 128) ? c : '?';
                }
                fits_update_key_str(fptr, key.c_str(),
                                    sanitized.c_str(),
                                    comment.c_str(), &status);
            }
            else if constexpr (std::is_same_v<T, int>) {
                // 整数处理
                fits_update_key_lng(fptr, key.c_str(),
                                    arg,
                                    comment.c_str(), &status);
            }
            else if constexpr (std::is_same_v<T, double>) {
                // 浮点处理
                fits_update_key_dbl(fptr, key.c_str(),
                                    arg,
                                    6,  // 小数位数
                                    comment.c_str(), &status);
            }
        }, value);

        if (status) break;
    }

    // 4. 处理每个CCD
    for (size_t i = 0; i < inputFiles.size(); ++i) {
        fitsfile *infptr = nullptr;
        fits_open_file(&infptr, inputFiles[i].c_str(), READONLY, &status);
        if (status) goto error;

        // 复制整个HDU（包括头和数据）
        fits_copy_hdu(infptr, fptr, 0, &status);
        //if (status) goto cleanup;

        // 更新扩展头标识
        fits_update_key_str(fptr, "EXTNAME", ("CCD" + std::to_string(i+1)).c_str(), nullptr, &status);
        fits_update_key_lng(fptr, "EXTVER", i+1, nullptr, &status);

        // 写入扩展HDU头信息（预留所有关键字位置）
        HeaderKeywords extKeywords = generateExtensionHeader4720(i, CCD_SN ,imagetype, imgrect,
                                                                 channel,EXPREQ);
        for (const auto& keyword : extKeywords) {
            const std::string& key = std::get<0>(keyword);
            const HeaderValue& value = std::get<1>(keyword);
            const std::string& comment = std::get<2>(keyword);

            std::visit([&](auto&& arg) {
                using T = std::decay_t<decltype(arg)>;

                if constexpr (std::is_same_v<T, std::string>) {
                    // 字符串处理
                    std::string sanitized;
                    for (char c : arg) {
                        sanitized += (isprint(c) && c < 128) ? c : '?';
                    }
                    fits_update_key_str(fptr, key.c_str(),
                                        sanitized.c_str(),
                                        comment.c_str(), &status);
                }
                else if constexpr (std::is_same_v<T, int>) {
                    // 整数处理
                    fits_update_key_lng(fptr, key.c_str(),
                                        arg,
                                        comment.c_str(), &status);
                }
                else if constexpr (std::is_same_v<T, double>) {
                    // 浮点处理
                    fits_update_key_dbl(fptr, key.c_str(),
                                        arg,
                                        6,  // 小数位数
                                        comment.c_str(), &status);
                }
            }, value);

            if (status) break;
        }

    cleanup:
        fits_close_file(infptr, &status);
        if (status) goto error;
    }

    // 5. 强制更新校验和
    fits_write_chksum(fptr, &status);
    fits_close_file(fptr, &status);
    return status == 0;

error:
    if (fptr) fits_close_file(fptr, &status);
    fits_report_error(stderr, status);
    return false;
}

// 读取二进制文件
std::vector<uint16_t> FitsConverter::readBinaryFile(const std::string& filename) {
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    if (!file) {
        throw std::runtime_error("无法打开文件: " + filename);
    }

    std::streamsize size = file.tellg();
    file.seekg(0, std::ios::beg);

    if (size % sizeof(uint16_t) != 0) {
        throw std::runtime_error("文件大小不是uint16_t的整数倍");
    }

    std::vector<uint16_t> buffer(size / sizeof(uint16_t));
    if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
        throw std::runtime_error("读取文件失败: " + filename);
    }

// 确保小端序读取（如果Python版本是小端序）
// 根据您的Python脚本使用的字节序决定是否需要交换
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    for (auto& val : buffer) {
        val = __builtin_bswap16(val);
    }
#endif

    return buffer;
}

// 分割数据到列
std::vector<std::vector<uint16_t>> FitsConverter::splitDataIntoColumns(
    const std::vector<uint16_t>& data, int numColumns) {

    if (data.size() % numColumns != 0) {
        throw std::runtime_error("数据长度必须是列数的整数倍");
    }

    size_t elementsPerColumn = data.size() / numColumns;
    std::vector<std::vector<uint16_t>> columns(numColumns, std::vector<uint16_t>(elementsPerColumn));

    // 更高效的列分割方式，确保顺序与Python一致
    for (size_t i = 0; i < elementsPerColumn; ++i) {
        for (int j = 0; j < numColumns; ++j) {
            columns[j][i] = data[i * numColumns + j];
        }
    }

    return columns;
}

// 重塑列数据 (4700x1250)
std::vector<std::vector<uint16_t>> FitsConverter::reshapeColumns(
    const std::vector<std::vector<uint16_t>>& columns, int rows, int cols) {

    std::vector<std::vector<uint16_t>> reshapedColumns;
    reshapedColumns.reserve(columns.size());

    for (const auto& col : columns) {
        if (col.size() != static_cast<size_t>(rows * cols)) {
            throw std::runtime_error("列数据大小与重塑尺寸不匹配");
        }

        std::vector<uint16_t> reshaped(rows * cols);
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                reshaped[i * cols + j] = col[j * rows + i]; // 转置重塑
            }
        }
        reshapedColumns.push_back(reshaped);
    }

    return reshapedColumns;
}

// 拼接列数据 (2行8列)，完全按照Python版本的逻辑
std::vector<uint16_t> FitsConverter::concatenateColumns(
    const std::vector<std::vector<uint16_t>>& columns) {

    const int numRows = 2;
    const int numColumnsPerRow = 8;
    const int colHeight = 4700;
    const int colWidth = 1250;

    if (columns.size() != 16) {
        throw std::runtime_error("需要16列数据");
    }

    // 验证每列数据大小
    for (const auto& col : columns) {
        if (col.size() != colHeight * colWidth) {
            throw std::runtime_error("列数据大小不正确");
        }
    }

    // 计算最终图像尺寸
    const int totalWidth = colWidth * numColumnsPerRow;
    const int totalHeight = colHeight * numRows;
    std::vector<uint16_t> combinedImage(totalWidth * totalHeight, 0);

    // 定义与Python版本完全相同的通道重排顺序
    const std::vector<int> reorder_lower = {2, 3, 0, 1, 6, 7, 4, 5};  // 对应Python的[3,4,1,2,7,8,5,6]
    const std::vector<int> reorder_upper = {5, 4, 7, 6, 1, 0, 3, 2};  // 对应Python的[c,d,a,b,g,h,e,f]

    for (int row = 0; row < numRows; ++row) {
        for (int col = 0; col < numColumnsPerRow; ++col) {
            // 计算原始通道索引
            int origChan;
            if (row == 0) {
                origChan = reorder_lower[col];
            } else {
                origChan = 8 + reorder_upper[col]; // 上排通道
            }

            if (origChan < 0 || origChan >= 16) {
                throw std::runtime_error("无效的通道索引");
            }

            const auto& column = columns[origChan];

            // 计算目标位置
            int destX = col * colWidth;
            int destY = row * colHeight;

            // 复制数据，同时应用翻转
            for (int y = 0; y < colHeight; ++y) {
                for (int x = 0; x < colWidth; ++x) {
                    // 计算源位置，考虑翻转
                    int srcY = y;
                    int srcX = x;

                    // 底行需要垂直翻转
                    if (row == 1) {
                        srcY = colHeight - 1 - y;
                    }

                    // 右半图像水平翻转 (通道4-7和12-15)
                    if (origChan % 8 >= 4) {
                        srcX = colWidth - 1 - x;
                    }

                    // 计算源位置索引
                    int srcPos = srcY * colWidth + srcX;

                    // 确保不越界
                    if (srcPos >= 0 && srcPos < static_cast<int>(column.size())) {
                        int destPos = (destY + y) * totalWidth + (destX + x);
                        if (destPos >= 0 && destPos < static_cast<int>(combinedImage.size())) {
                            combinedImage[destPos] = column[srcPos];
                        }
                    }
                }
            }
        }
    }

    return combinedImage;
}

// 写入FITS文件
bool FitsConverter::writeFitsFile(const std::string& filePath,
                                  const std::vector<uint16_t>& imageData,
                                  int width, int height,
                                  const std::vector<std::pair<std::string, std::string>>& keywords) {
    fitsfile* fitsFile = nullptr;
    int status = 0;

    // 删除已存在的文件
    remove(filePath.c_str());

    // 创建新FITS文件
    fits_create_file(&fitsFile, ("!" + filePath).c_str(), &status);
    if (status) {
        char errText[FLEN_STATUS];
        fits_get_errstatus(status, errText);
        throw std::runtime_error("无法创建FITS文件: " + std::string(errText));
    }

    // 创建图像HDU
    long naxes[2] = {width, height};
    fits_create_img(fitsFile, USHORT_IMG, 2, naxes, &status);
    if (status) {
        fits_report_error(stderr, status);
        throw std::runtime_error("无法创建图像HDU");
    }

    // 添加关键字
    for (const auto& kv : keywords) {
        std::string key = kv.first.substr(0, 8); // FITS关键字最多8字符
        std::string value = kv.second;
        std::string comment = "";

        fits_update_key(fitsFile, TSTRING, key.c_str(), (void*)value.c_str(), comment.c_str(), &status);
        if (status) {
            fits_report_error(stderr, status);
            throw std::runtime_error("无法写入关键字");
        }
    }

    // 写入图像数据
    long fpixel[2] = {1, 1};
    fits_write_pix(fitsFile, TUSHORT, fpixel, imageData.size(), (void*)imageData.data(), &status);
    if (status) {
        fits_report_error(stderr, status);
        throw std::runtime_error("无法写入图像数据");
    }

    // 关闭FITS文件
    fits_close_file(fitsFile, &status);
    if (status) {
        fits_report_error(stderr, status);
        throw std::runtime_error("无法关闭FITS文件");
    }

    return true;
}

// 主处理函数（匹配Python脚本）
bool FitsConverter::processBinToFits(const std::string& binPath,
                                     const std::string& outputDir,
                                     const std::string& baseFilename,
                                     const std::vector<std::pair<std::string, std::string>>& keywords) {
    try {
        // 确保输出目录存在
        if (mkdir(outputDir.c_str(), 0755) != 0 && errno != EEXIST) {
            throw std::runtime_error("无法创建输出目录: " + outputDir);
        }

        // 1. 读取二进制文件
        auto data = readBinaryFile(binPath);

        // 2. 分割数据为16列
        auto columns = splitDataIntoColumns(data, 16);

        // 3. 重塑每列为4700x1250
        auto reshapedColumns = reshapeColumns(columns, 4700, 1250);

        // 4. 拼接为2行8列的图像，完全按照Python版本的逻辑
        auto combinedImage = concatenateColumns(reshapedColumns);

        // 5. 保存为FITS文件
        std::string outputFile = outputDir + "/" + baseFilename + ".fits";
        bool success = writeFitsFile(outputFile, combinedImage, 1250 * 8, 4700 * 2, keywords);

        return success;
    } catch (const std::exception& e) {
        throw std::runtime_error("BIN转FITS失败: " + std::string(e.what()));
    }
}

void MainWindow::processCameraHeader(const QString& response)
{

    QMap<QString, QString> params;
    QStringList parts = response.split('$');

    for (const QString& part : parts) {
        int eqPos = part.indexOf('=');
        if (eqPos > 0) {
            QString key = part.left(eqPos);
            QString value = part.mid(eqPos+1);
            params[key] = value;
        }
    }

    // 更新当前曝光信息
    m_currentExpInfo.tag = params.value("tag");
    m_currentExpInfo.expStart = parseCameraTimestamp(params.value("exp_start"));
    m_currentExpInfo.expEnd = parseCameraTimestamp(params.value("exp_end"));
    m_currentExpInfo.readoutStart = parseCameraTimestamp(params.value("readout_start"));

    // 验证时间戳有效性
    if (!m_currentExpInfo.expStart.isValid() ||
        !m_currentExpInfo.expEnd.isValid() ||
        !m_currentExpInfo.readoutStart.isValid()) {
        LOG_CAMERA("Parsed an invalid timestamp");
        m_currentExpInfo = ExposureInfo(); // 重置为无效状态
    }
}
void MainWindow::processCameraHeader4720(const QString& response)
{

    QMap<QString, QString> params;
    QStringList parts = response.split('$');

    for (const QString& part : parts) {
        int eqPos = part.indexOf('=');
        if (eqPos > 0) {
            QString key = part.left(eqPos);
            QString value = part.mid(eqPos+1);
            params[key] = value;
        }
    }

    // 更新当前曝光信息
    m_currentExpInfo4720.tag = params.value("tag");
    m_currentExpInfo4720.expStart = parseCameraTimestamp(params.value("exp_start"));
    m_currentExpInfo4720.expEnd = parseCameraTimestamp(params.value("exp_end"));
    m_currentExpInfo4720.readoutStart = parseCameraTimestamp(params.value("readout_start"));

    // 验证时间戳有效性
    if (!m_currentExpInfo4720.expStart.isValid() ||
        !m_currentExpInfo4720.expEnd.isValid() ||
        !m_currentExpInfo4720.readoutStart.isValid()) {
        LOG_CAMERA("Parsed an invalid timestamp");
        m_currentExpInfo4720 = ExposureInfo(); // 重置为无效状态
    }
}
bool MainWindow::convertBinToFits(const QString& binPath,
                                  const QString& fitsPath,
                                  const QString& baseFilename,
                                  const std::vector<std::pair<std::string, std::string>>& keywords)
{
    // 准备关键字列表（包含原有keywords和时间戳）
    std::vector<std::pair<std::string, std::string>> allKeywords = keywords;

    // 如果当前曝光信息匹配，添加时间戳关键字
    if (!m_currentExpInfo.tag.isEmpty() && m_currentExpInfo.tag == baseFilename.right(11)) {
        // 计算曝光时间（秒）
        double expTime = m_currentExpInfo.expStart.msecsTo(m_currentExpInfo.expEnd) / 1000.0;

        // 添加时间戳关键字
        allKeywords.emplace_back("EXPSTART", m_currentExpInfo.expStart.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
        allKeywords.emplace_back("EXPEND", m_currentExpInfo.expEnd.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
        allKeywords.emplace_back("READOUTS", m_currentExpInfo.readoutStart.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
        allKeywords.emplace_back("EXPTIME", QString::number(expTime, 'f', 3).toStdString());

        LOG_CAMERA(QString("Add to FITS Header: EXPSTART=%1, EXPEND=%2, READOUTS=%3, EXPTIME=%4")
                       .arg(m_currentExpInfo.expStart.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(m_currentExpInfo.expEnd.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(m_currentExpInfo.readoutStart.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(expTime));
    }

    // 原有的转换逻辑（使用包含时间戳的allKeywords）
    try {
        bool result = FitsConverter::processBinToFits(
            binPath.toStdString(),
            fitsPath.toStdString(),
            baseFilename.toStdString(),
            allKeywords  // 传入所有关键字（包含时间戳）
            );

        if (!result) {
            LOG_CAMERA(QString("BIN2FITS fialed: %1").arg(binPath));
            return false;
        }

        QString fitsFile = fitsPath + "/" + baseFilename + ".fits";
        if (!QFile::exists(fitsFile)) {
            LOG_CAMERA(QString("The FITS file has not been generated: %1").arg(fitsFile));
            return false;
        }

        LOG_CAMERA(QString("BIN2FITS successfully: %1 -> %2").arg(binPath).arg(fitsFile));
        return true;
    } catch (const std::exception& e) {
        LOG_CAMERA(QString("An exception occurred during the conversion process: %1").arg(e.what()));
        return false;
    }
}

void MainWindow::on_btnmergefits_clicked(){
    /*QStringList fitsFilePaths;
    QString fitsFile0 = fitsSavePath + "/" + "00" + ".fits";
    QString fitsFile1 = fitsSavePath + "/" + "01" + ".fits";
    QString fitsFile2 = fitsSavePath + "/" + "02" + ".fits";
    QString fitsFile3 = fitsSavePath + "/" + "03" + ".fits";
    fitsFilePaths.append(fitsFile0);
    fitsFilePaths.append(fitsFile1);
    fitsFilePaths.append(fitsFile2);
    fitsFilePaths.append(fitsFile3);

    QString mergedFitsPath = fitsSavePath + "/" + currentBaseFilename + "_merged.fits";
    if (mergeFitsFiles(fitsFilePaths, mergedFitsPath)) {
        LOG_CAMERA(QString("成功合并FITS文件: %1").arg(mergedFitsPath));

        // 可选：删除原始单个FITS文件
        //for (const QString &fitsFile : fitsFilePaths) {
        //QFile::remove(fitsFile);
        //}
    } else {
        LOG_CAMERA("合并FITS文件失败");
    }*/

}
/*0827zhushi
bool MainWindow::mergeFitsFiles(const QStringList& inputFiles, const QString& outputFile)
{
    fitsfile *outfits = nullptr;
    int status = 0;

    // 删除已存在的输出文件
    remove(outputFile.toLocal8Bit().constData());

    // 创建新的FITS文件
    if (fits_create_file(&outfits, ("!" + outputFile).toLocal8Bit().constData(), &status)) {
        char errText[FLEN_STATUS];
        fits_get_errstatus(status, errText);
        LOG_CAMERA(QString("Failed to create a merged FITS file: %1").arg(errText));
        return false;
    }

    // 逐个添加输入文件作为HDU
    for (const QString &inputFile : inputFiles) {
        fitsfile *infits = nullptr;

        // 打开输入文件
        if (fits_open_file(&infits, inputFile.toLocal8Bit().constData(), READONLY, &status)) {
            char errText[FLEN_STATUS];
            fits_get_errstatus(status, errText);
            LOG_CAMERA(QString("Failed to open the input FITS file: %1 - %2").arg(inputFile).arg(errText));
            fits_close_file(outfits, &status);
            return false;
        }

        // 获取输入HDU信息
        int hdutype;
        if (fits_get_hdu_type(infits, &hdutype, &status)) {
            fits_close_file(infits, &status);
            fits_close_file(outfits, &status);
            return false;
        }

        // 复制HDU到输出文件
        if (fits_copy_hdu(infits, outfits, 0, &status)) {
            char errText[FLEN_STATUS];
            fits_get_errstatus(status, errText);
            LOG_CAMERA(QString("Failed to copy HDU.: %1 - %2").arg(inputFile).arg(errText));
            fits_close_file(infits, &status);
            fits_close_file(outfits, &status);
            return false;
        }

        // 关闭输入文件
        fits_close_file(infits, &status);
    }

    // 添加合并信息到历史记录
    QString history = QString("Merged from %1 files at %2")
                          .arg(inputFiles.size())
                          .arg(QDateTime::currentDateTime().toString(Qt::ISODate));
    fits_write_history(outfits, history.toLocal8Bit().constData(), &status);

    // 关闭输出文件
    if (fits_close_file(outfits, &status)) {
        char errText[FLEN_STATUS];
        fits_get_errstatus(status, errText);
        LOG_CAMERA(QString("Failed to close the merged FITS file.: %1").arg(errText));
        return false;
    }

    return true;
}*/
bool MainWindow::mergeFitsFiles(const QStringList& inputFiles, const QString& outputFile, const QString& filename, const QString& pathname,
                                const QString& date_,
                                const double& TEMP_1,
                                const double& TEMP_2,
                                const double& TEMP_3,
                                const double& TEMP_4,
                                const double& TEMP,
                                const QString& CCD_SN,
                                const QString& imagetype,
                                const QString& imgrect,
                                const QString& readmode,
                                const QString& channel,
                                const double& EXPTIME, const double& EXPREQ, const QString& EXPSTA, const QString& EXPEND) {
    // 转换QStringList到std::vector<std::string>
    std::vector<std::string> stdInputFiles;
    for (const auto& file : inputFiles) {
        stdInputFiles.push_back(file.toStdString());
    }

    // 调用合并函数（仅预留头信息位置）
    return mergeMephistoFitsFiles(stdInputFiles, outputFile.toStdString(), filename, pathname, date_,
                                  TEMP_1,
                                  TEMP_2,
                                  TEMP_3,
                                  TEMP_4,
                                  TEMP,
                                  CCD_SN,
                                  imagetype,
                                  imgrect,
                                  readmode,
                                  channel,
                                  EXPTIME,  EXPREQ, EXPSTA, EXPEND);
}
bool MainWindow::mergeMephistoFitsFiles(const std::vector<std::string>& inputFiles,
                                        const std::string& outputFile, const QString& filename, const QString& pathname,
                                        const QString& date_,
                                        const double& TEMP_1,
                                        const double& TEMP_2,
                                        const double& TEMP_3,
                                        const double& TEMP_4,
                                        const double& TEMP,
                                        const QString& CCD_SN,
                                        const QString& imagetype,
                                        const QString& imgrect,
                                        const QString& readmode,
                                        const QString& channel,
                                        const double& EXPTIME, const double& EXPREQ, const QString& EXPSTA, const QString& EXPEND) {
    fitsfile *fptr = nullptr;
    int status = 0;

    // 1. 创建文件（强制覆盖）
    fits_create_file(&fptr, ("!" + outputFile).c_str(), &status);
    //if (status) goto error;

    // 2. 创建标准主HDU
    long naxes[0] = {};
    fits_create_img(fptr, 8, 0, naxes, &status);  // BITPIX=8, NAXIS=0
    //if (status) goto error;

    // 3. 写入必须的头信息（顺序敏感！）
    //fits_write_imghdr(fptr);  // 写入标准头结构
    fits_update_key_log(fptr, "SIMPLE", TRUE, "conforms to FITS standard", &status);
    fits_update_key_lng(fptr, "BITPIX", 8, nullptr, &status);
    fits_update_key_lng(fptr, "NAXIS", 0, nullptr, &status);
    fits_update_key_log(fptr, "EXTEND", TRUE, nullptr, &status);
    fits_update_key_lng(fptr, "NEXTEND", 4, nullptr, &status);

    // 3. 写入主HDU头信息（预留所有关键字位置）
    HeaderKeywords primaryKeywords = generatePrimaryHeader(filename, pathname, imagetype, date_, readmode, channel,
                                                           TEMP_1, TEMP_2, TEMP_3, TEMP_4, TEMP, EXPTIME, EXPREQ, EXPSTA, EXPEND);
    for (const auto& keyword : primaryKeywords) {
        const std::string& key = std::get<0>(keyword);
        const HeaderValue& value = std::get<1>(keyword);
        const std::string& comment = std::get<2>(keyword);

        std::visit([&](auto&& arg) {
            using T = std::decay_t<decltype(arg)>;

            if constexpr (std::is_same_v<T, std::string>) {
                // 字符串处理
                std::string sanitized;
                for (char c : arg) {
                    sanitized += (isprint(c) && c < 128) ? c : '?';
                }
                fits_update_key_str(fptr, key.c_str(),
                                    sanitized.c_str(),
                                    comment.c_str(), &status);
            }
            else if constexpr (std::is_same_v<T, int>) {
                // 整数处理
                fits_update_key_lng(fptr, key.c_str(),
                                    arg,
                                    comment.c_str(), &status);
            }
            else if constexpr (std::is_same_v<T, double>) {
                // 浮点处理
                fits_update_key_dbl(fptr, key.c_str(),
                                    arg,
                                    6,  // 小数位数
                                    comment.c_str(), &status);
            }
        }, value);

        if (status) break;
    }
    /*for (const auto& keyword : primaryKeywords) {
        if (keyword.first == "SIMPLE" || keyword.first == "EXTEND") {
            fits_update_key_log(fptr, keyword.first.c_str(),
                                keyword.second == "T", nullptr, &status);
        }
        else if (keyword.first == "BITPIX" || keyword.first == "NAXIS" ||
                 keyword.first == "NEXTEND" || keyword.first == "NCCDS" ||
                 keyword.first == "NAMPS" || keyword.first == "HBIN" ||
                 keyword.first == "VBIN") {
            fits_update_key_lng(fptr, keyword.first.c_str(),
                                std::stol(keyword.second), nullptr, &status);
        }
        else if (keyword.first == "TEMP" || keyword.first == "TEMP_1" ||
                 keyword.first == "TEMP_2" || keyword.first == "TEMP_3" ||
                 keyword.first == "TEMP_4" || keyword.first == "OBSLON" ||
                 keyword.first == "OBSLAT" || keyword.first == "AIRMASS" ||
                 keyword.first == "EXPTIME" || keyword.first == "EXPREQ") {
            fits_update_key_flt(fptr, keyword.first.c_str(),
                                keyword.second.empty() ? 0.0 : std::stof(keyword.second),
                                6, nullptr, &status);
        }
        else if (keyword.first == "OBSALT") {
            fits_update_key_lng(fptr, keyword.first.c_str(),
                                keyword.second.empty() ? 0 : std::stol(keyword.second),
                                nullptr, &status);
        }
        else {
            fits_update_key_str(fptr, keyword.first.c_str(),
                                keyword.second.empty() ? "" : const_cast<char*>(keyword.second.c_str()),
                                nullptr, &status);
        }

        if (status) break;
    }

    if (status) {
        fits_report_error(stderr, status);
        fits_close_file(fptr, &status);
        return false;
    }*/

    // 4. 处理每个CCD
    for (size_t i = 0; i < inputFiles.size(); ++i) {
        fitsfile *infptr = nullptr;
        fits_open_file(&infptr, inputFiles[i].c_str(), READONLY, &status);
        if (status) goto error;

        // 复制整个HDU（包括头和数据）
        fits_copy_hdu(infptr, fptr, 0, &status);
        //if (status) goto cleanup;

        // 更新扩展头标识
        fits_update_key_str(fptr, "EXTNAME", ("CCD" + std::to_string(i+1)).c_str(), nullptr, &status);
        fits_update_key_lng(fptr, "EXTVER", i+1, nullptr, &status);

        /*fits_update_key_log(fptr, "SIMPLE", TRUE, "conforms to FITS standard", &status);
        fits_update_key_lng(fptr, "BITPIX", 16, nullptr, &status);
        fits_update_key_lng(fptr, "NAXIS", 0, nullptr, &status);*/
        //fits_update_key_log(fptr, ", TRUE, nullptr, &status);
        //fits_update_key_lng(fptr, "//", 4, nullptr, &status);


        // 写入扩展HDU头信息（预留所有关键字位置）
        HeaderKeywords extKeywords = generateExtensionHeader(i, CCD_SN ,imagetype, imgrect,
                                                             readmode, channel, EXPTIME, EXPREQ, EXPSTA, EXPEND);
        for (const auto& keyword : extKeywords) {
            const std::string& key = std::get<0>(keyword);
            const HeaderValue& value = std::get<1>(keyword);
            const std::string& comment = std::get<2>(keyword);

            std::visit([&](auto&& arg) {
                using T = std::decay_t<decltype(arg)>;

                if constexpr (std::is_same_v<T, std::string>) {
                    // 字符串处理
                    std::string sanitized;
                    for (char c : arg) {
                        sanitized += (isprint(c) && c < 128) ? c : '?';
                    }
                    fits_update_key_str(fptr, key.c_str(),
                                        sanitized.c_str(),
                                        comment.c_str(), &status);
                }
                else if constexpr (std::is_same_v<T, int>) {
                    // 整数处理
                    fits_update_key_lng(fptr, key.c_str(),
                                        arg,
                                        comment.c_str(), &status);
                }
                else if constexpr (std::is_same_v<T, double>) {
                    // 浮点处理
                    fits_update_key_dbl(fptr, key.c_str(),
                                        arg,
                                        6,  // 小数位数
                                        comment.c_str(), &status);
                }
            }, value);

            if (status) break;
        }
        /*for (const auto& keyword : extKeywords) {
            if (keyword.first == "SIMPLE") {
                fits_update_key_log(fptr, keyword.first.c_str(),
                                    keyword.second == "T", nullptr, &status);
            }
            else if (keyword.first == "BITPIX" || keyword.first == "NAXIS" ||
                     keyword.first == "EXTVER" || keyword.first == "SATURATE" ||
                     keyword.first == "NAXIS1" || keyword.first == "NAXIS2" ||
                     keyword.first == "PIXSIZE1" || keyword.first == "PIXSIZE2") {
                fits_update_key_lng(fptr, keyword.first.c_str(),
                                    keyword.second.empty() ? 0 : std::stol(keyword.second),
                                    nullptr, &status);
            }
            else if (keyword.first == "TEMP_" + std::to_string(i+1) ||
                     keyword.first == "PIXSCAL1" || keyword.first == "PIXSCAL2" ||
                     keyword.first == "EXPTIME" || keyword.first == "EXPREQ" ||
                     keyword.first == "CRVAL1" || keyword.first == "CRVAL2" ||
                     keyword.first == "CRPIX1" || keyword.first == "CRPIX2" ||
                     keyword.first == "CD1_1" || keyword.first == "CD1_2" ||
                     keyword.first == "CD2_1" || keyword.first == "CD2_2") {
                fits_update_key_flt(fptr, keyword.first.c_str(),
                                    keyword.second.empty() ? 0.0 : std::stof(keyword.second),
                                    6, nullptr, &status);
            }
            else {
                fits_update_key_str(fptr, keyword.first.c_str(),
                                    keyword.second.empty() ? "" : const_cast<char*>(keyword.second.c_str()),
                                    nullptr, &status);
            }

            if (status) break;
        }*/

    cleanup:
        fits_close_file(infptr, &status);
        if (status) goto error;
    }

    // 5. 强制更新校验和
    fits_write_chksum(fptr, &status);
    fits_close_file(fptr, &status);
    return status == 0;

error:
    if (fptr) fits_close_file(fptr, &status);
    fits_report_error(stderr, status);
    return false;
}




// 收集手动拍摄的头信息
std::vector<std::pair<std::string, std::string>> MainWindow::collectManualHeaderInfo() {
    std::vector<std::pair<std::string, std::string>> keywords;

    // 标准关键字
    keywords.push_back({"INSTRUME", "BLUE"}); // TODO: blue should be replaced by a string var configured from .ini
    keywords.push_back({"OBSERVER", "MEPHISTO"});
    keywords.push_back({"TELESCOP", "MEPHISTO"});
    keywords.push_back({"EXPTIME", QString::number(ui->doubleSpinBoxExposureTime->value()).toStdString()});
    // keywords.push_back({"DATE-OBS", QDateTime::currentDateTime().toString(Qt::ISODate).toStdString()});
    keywords.push_back({"DATE-OBS", QDateTime::currentDateTimeUtc().toString(Qt::ISODate).toStdString()});

    // 从界面获取手动输入的信息
    if (!ui->lineEditObjName->text().isEmpty()) {
        keywords.push_back({"OBJ_NAME", ui->lineEditObjName->text().toStdString()});
    }
    if (!ui->lineEditFwName->text().isEmpty()) {
        keywords.push_back({"FW_NAME", ui->lineEditFwName->text().toStdString()});
    }
    if (!ui->lineEditImgNum->text().isEmpty()) {
        keywords.push_back({"IMG_NUM", ui->lineEditImgNum->text().toStdString()});
    }
    QString frameType = ui->comBoxframetype->currentData().toString();
    std::string darkMode = (frameType == "bs" || frameType == "dk") ? "1" : "0";
    // 其他复选框状态

    if (m_currentExpInfo.isValid()) {
        keywords.push_back({"RATE", m_currentExpInfo.readoutRate.toStdString()});
    }
    keywords.push_back({"IMGTYPE", frameType.toStdString()});
    keywords.push_back({"DARK", darkMode});
    keywords.push_back({"PINNING", ui->checkBoxPinning->isChecked() ? "1" : "0"});
    keywords.push_back({"DUMP", ui->checkBoxDump->isChecked() ? "1" : "0"});

    return keywords;
}
/*
std::vector<std::pair<std::string, std::string>> MainWindow::collectManualHeaderInfo4720() {
    std::vector<std::pair<std::string, std::string>> keywords;

    // 标准关键字

    keywords.push_back({"EXPTIME", QString::number(ui->doubleSpinBoxExposureTime->value()).toStdString()});
    keywords.push_back({"DATE-OBS", QDateTime::currentDateTime().toString(Qt::ISODate).toStdString()});

    // 从界面获取手动输入的信息
    if (!ui->lineEditObjName->text().isEmpty()) {
        keywords.push_back({"OBJ_NAME", ui->lineEditObjName->text().toStdString()});
    }
    if (!ui->lineEditFwName->text().isEmpty()) {
        keywords.push_back({"FW_NAME", ui->lineEditFwName->text().toStdString()});
    }
    if (!ui->lineEditImgNum->text().isEmpty()) {
        keywords.push_back({"IMG_NUM", ui->lineEditImgNum->text().toStdString()});
    }
    QString frameType = ui->comBoxframetype->currentData().toString();
    std::string darkMode = (frameType == "bs" || frameType == "dk") ? "1" : "0";
    // 其他复选框状态

    if (m_currentExpInfo.isValid()) {
        keywords.push_back({"RATE", m_currentExpInfo.readoutRate.toStdString()});
    }
    keywords.push_back({"IMGTYPE", frameType.toStdString()});
    keywords.push_back({"DARK", darkMode});
    keywords.push_back({"PINNING", ui->checkBoxPinning->isChecked() ? "1" : "0"});
    keywords.push_back({"DUMP", ui->checkBoxDump->isChecked() ? "1" : "0"});

    return keywords;
}*/
double MainWindow::calculateFitsMedian(const QString& fitsPath)
{
    fitsfile* fitsFile = nullptr;
    int status = 0;
    long fpixel[2] = {1, 1};
    long naxes[2] = {0, 0};
    double median = -999.0;

    // 1. 打开FITS文件
    QByteArray filePath = QDir::toNativeSeparators(fitsPath).toLocal8Bit();
    if (fits_open_file(&fitsFile, filePath.constData(), READONLY, &status)) {
        char errText[FLEN_STATUS];
        fits_get_errstatus(status, errText);
        throw std::runtime_error(QString("打开FITS文件失败: %1").arg(errText).toStdString());
    }

    // 2. 获取图像尺寸
    if (fits_get_img_size(fitsFile, 2, naxes, &status)) {
        fits_close_file(fitsFile, &status);
        throw std::runtime_error("获取图像尺寸失败");
    }

    long npixels = naxes[0] * naxes[1];
    if (npixels <= 0) {
        fits_close_file(fitsFile, &status);
        throw std::runtime_error("无效的图像尺寸");
    }

    // 3. 读取图像数据
    std::vector<float> buffer(npixels);
    if (fits_read_pix(fitsFile, TFLOAT, fpixel, npixels, nullptr, buffer.data(), nullptr, &status)) {
        fits_close_file(fitsFile, &status);
        throw std::runtime_error("读取图像数据失败");
    }

    // 4. 计算中值
    auto mid = buffer.begin() + npixels / 2;
    std::nth_element(buffer.begin(), mid, buffer.end());
    median = *mid;

    // 如果是偶数个像素，取中间两个的平均值
    if (npixels % 2 == 0) {
        auto mid2 = std::max_element(buffer.begin(), mid);
        median = (*mid + *mid2) / 2.0;
    }

    // 5. 关闭文件
    fits_close_file(fitsFile, &status);

    return median;
}

bool MainWindow::updateFitsHeader(const QString& fitsPath, const QJsonObject& headerData)
{
    fitsfile* fitsFile = nullptr;
    int status = 0;
    char errText[FLEN_STATUS] = {0};

    if(!QFile::exists(fitsPath)) {
        LOG_CAMERA(fitsPath + QString(" dose not exist!"));
    } else {
        LOG_CAMERA(QString(">>>> found: ")+fitsPath);
    }

    // 1. 打开文件
    QByteArray filePath = QDir::toNativeSeparators(fitsPath).toLocal8Bit();
    LOG_CAMERA(filePath);
    // LOG_CAMERA(&fitsFile);
    if (fits_open_file(&fitsFile, filePath.constData(), READWRITE, &status)) {
        fits_get_errstatus(status, errText);
        LOG_CAMERA(QString("Failed to open the FITS file.: %1").arg(errText));
        return false;
    }

    // 2. 更新关键字
    for (auto it = headerData.begin(); it != headerData.end(); ++it) {
        QString key = it.key();
        QString value = it.value().isString() ? it.value().toString()
                                              : it.value().toVariant().toString();

        QByteArray keyData = key.toLocal8Bit();
        QByteArray valueData = value.toLocal8Bit();

        // 先删除已存在的关键字（忽略错误）
        fits_delete_key(fitsFile, keyData.constData(), &status);
        status = 0;

        // 写入新关键字
        if (fits_update_key(fitsFile, TSTRING,
                            keyData.constData(),
                            valueData.data(),  // 注意这里用 data() 而不是 constData()
                            nullptr, &status)) {
            fits_get_errstatus(status, errText);
            LOG_CAMERA(QString("Failed to update the keyword: %1").arg(errText));
            status = 0;
            continue;
        }
    }

    // 3. 关闭文件
    if (fits_close_file(fitsFile, &status)) {
        fits_get_errstatus(status, errText);
        LOG_GENERAL(QString("Failed to close the FITS file: %1").arg(errText));
        return false;
    }

    return true;
}
